package whut.problem;

import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.stat.StatUtils;
import org.apache.commons.math3.util.FastMath;
import whut.commons.IProblemSet;
import whut.commons.Location;
import whut.pso.PSOUtility;

/**
 * Created by Justin on 2018-04-17
 */
public class FuzzyGamesProblemSet implements IProblemSet {
    public final double LOC_LOW = 0;
    public final double LOC_HIGH = 1;
    public final double VEL_LOW = -1;
    public final double VEL_HIGH = 1;
    public final int PROBLEM_DIMENSION = 6;
    public final int LOC_DIMENSION = 2;

    public final double ERR_TOLERANCE = 1E-6; // the smaller the tolerance, the more accurate the result,
    // but the number of iteration is increased

//    public static double[][] A = new double[][]{{0, -5},{-5, 1}};   //二阶
//    public static double[][] B = new double[][]{{0, 0},{-5, -1}};
//    public static double[][] P = new double[][]{{0, 0, -5, 10}, {0, 0, -5, 10}}; //矩阵坐标X, Y，系数Base, scale

    public static double[][] A = new double[][]{{2, 7, 9}, {4, 6, 3}, {3, 9, 7}};   //三阶
    public static double[][] B = new double[][]{{1, 4, 8}, {8, 2, 5}, {2, 10, 2}};
    public static double[][] P = new double[][]{{1, 1, 0, 10}, {1, 1, 0, 10}};

    @Override
    public double getLOC_LOW() {
        return LOC_LOW;
    }

    @Override
    public double getLOC_HIGH() {
        return LOC_HIGH;
    }

    @Override
    public double getVEL_LOW() {
        return VEL_LOW;
    }

    @Override
    public double getVEL_HIGH() {
        return VEL_HIGH;
    }

    @Override
    public double getERR_TOLERANCE() {
        return ERR_TOLERANCE;
    }

    @Override
    public int getPROBLEM_DIMENSION() {
        return PROBLEM_DIMENSION;
    }

    @Override
    public int getLOC_DIMENSION() {
        return LOC_DIMENSION;
    }
    
    @Override
    public double evaluate(Location location) {
        double[] loc = location.getLoc();
        double[][] xyz = PSOUtility.getXYZ(loc, LOC_DIMENSION, PROBLEM_DIMENSION - 2 * LOC_DIMENSION);

        double[] x = xyz[0];
        double[] y = xyz[1];
        double[] z = xyz[2];

        RealMatrix matA = getMat(A, P[0], z[0]);
        RealMatrix matB = getMat(B, P[1], z[1]);

        RealMatrix X = new Array2DRowRealMatrix(x);
        RealMatrix Y = new Array2DRowRealMatrix(y);               

        RealMatrix a1 = matA.multiply(Y);
        RealMatrix a2 = (X.transpose()).multiply(a1);
        double[][] a3 = a1.scalarAdd(-a2.getEntry(0, 0)).transpose().getData();
        double m1 = FastMath.max(0, StatUtils.max(a3[0]));

        RealMatrix b1 = (X.transpose()).multiply(matB);//matB.multiply(X);
        RealMatrix b2 = b1.multiply(Y);
        double[][] b3 = b1.scalarAdd(-b2.getEntry(0, 0)).getData();//这里不需要transpose(),
        double m2 = FastMath.max(0, StatUtils.max(b3[0]));

        double benefitA = a2.getEntry(0, 0);
        double benefitB = b2.getEntry(0, 0);
        location.setExtended(new double[]{benefitA, benefitB});

        return m1 + m2;
    }

    public RealMatrix getMat(double[][] AB, double[] params, double value){
        RealMatrix matA = new Array2DRowRealMatrix(AB);
        int x = (int)params[0];
        int y = (int)params[1];
        double base = params[2];
        double scale = params[3];
        double scaleValue = base + scale * value;
        matA.setEntry(x, y, scaleValue);
        return matA;
    }
}
